package com.basin.micro.gateway.config;

import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.stp.StpUtil;
import com.basin.common.core.exception.BasinException;
import com.basin.common.core.response.ResponseResult;
import com.basin.dubbo.pojo.UriConfigInfo;
import com.basin.micro.gateway.cache.AuthCache;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Objects;

/**
 * @author: Sam ZHONG | sammy1997@aliyun.com
 */
@Configuration
@Slf4j
public class AuthConfig {
    @Resource
    private AuthCache authCache;

    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 拦截所有请求
                .addInclude("/**")
                .addExclude("/favicon.ico", "/auth/auth/login")
                .setAuth(r -> {
                    // IP白名单验证
                    ServerHttpRequest request = SaReactorSyncHolder.getContext().getRequest();
                    InetSocketAddress remoteAddress = request.getRemoteAddress();
                    String ip = Objects.requireNonNull(remoteAddress).getHostString();
                    boolean isWhiteIp = this.authCache.getWhiteIps().contains(ip);
                    if (isWhiteIp || ip.equals("127.0.0.1")) {
                        log.info("当前ip[{}]已在IP白名单中，已放行！", ip);
                        return;
                    }
                    // uri白名单验证
                    String currentUri = SaHolder.getRequest().getRequestPath();
                    boolean isWhiteUri = this.authCache.getWhiteUris().contains(currentUri);
                    if (isWhiteUri) {
                        log.info("当前uri[{}]已在URI白名单中，已放行！", currentUri);
                        return;
                    }
                    if (!StpUtil.isLogin()) {
                        throw new BasinException("请先登录系统");
                    }
                    Object loginId = StpUtil.getLoginIdAsString();
                    // 用户白名单验证
                    boolean isWhiteUser = this.authCache.getWhiteUserIds().contains(loginId);
                    if (isWhiteUser) {
                        log.info("当前用户[{}]已在用户白名单中，已放行！", loginId);
                        return;
                    }
                    // 获取URI对应的配置
                    UriConfigInfo uriConfig = this.authCache.getUriConfig(currentUri);
                    // 仅需登录的URI此处直接放行
                    if (!uriConfig.getOnlyLogin()) {
                        // 校验模式
                        switch (uriConfig.getCheckEnum()) {
                            case ONLY_ROLE -> validWhenOnlyRole(uriConfig);
                            case ONLY_PERMISSION -> validWhenOnlyPerms(uriConfig);
                            case BOTH_ROLE_AND_PERMISSION -> validWhenBothRoleAndPerms(uriConfig);
                            default ->
                                    throw new BasinException("未知的鉴权模式[" + uriConfig.getCheckEnum() + ":" + uriConfig.getCheckEnum().ordinal() + "]");
                        }
                    }
                })
                .setError(error -> {
                    log.error("sa-token鉴权失败", error);
                    return ResponseResult.fail(HttpStatus.SC_UNAUTHORIZED, "unauthorized failed");
                });
    }

    private static void validWhenBothRoleAndPerms(UriConfigInfo uriConfig) {
        validWhenOnlyRole(uriConfig);
        validWhenOnlyPerms(uriConfig);
    }

    private static void validWhenOnlyPerms(UriConfigInfo uriConfig) {
        SaMode saMode = uriConfig.getPermCheckMode();
        List<String> perms = uriConfig.getPerms();
        if (saMode == SaMode.AND) {
            StpUtil.checkPermissionAnd(perms.toArray(new String[0]));
        }
        if (saMode == SaMode.OR) {
            StpUtil.checkPermissionOr(perms.toArray(new String[0]));
        }
    }

    private static void validWhenOnlyRole(UriConfigInfo uriConfig) {
        SaMode saMode = uriConfig.getRoleCheckMode();
        List<String> roles = uriConfig.getRoles();
        if (saMode == SaMode.AND) {
            StpUtil.checkRoleAnd(roles.toArray(new String[0]));
        }
        if (saMode == SaMode.OR) {
            StpUtil.checkRoleOr(roles.toArray(new String[0]));
        }
    }
}
